package tabulation

import (
	"bufio"
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"gobindc_test/server/model"
	"io"
	"log"
	"os"
	"strings"
	"time"
)

func InsertTabulationInRam(data string, sqList *model.SqList) {
	myData := model.MyData{
		Message:    data,
		CreateTime: time.Now(),
	}
	// 如果队列没满
	i := len(sqList.DataList)
	if i > sqList.Length {
		sqList.DataList[sqList.Length] = &myData
		sqList.Length++
	} else {
		// 如果队列满了，就删除第一个数据，并将之后的元素前移
		makeTabulationLeft(sqList)
		sqList.DataList[sqList.Length-1] = &myData
	}
}

// 从队列中取出数据
func GetDataFromTabulationInRam(sqList *model.SqList) (*model.MyData, error) {
	if sqList.Length > 0 {
		data := sqList.DataList[0]
		makeTabulationLeft(sqList)
		sqList.Length--
		return data, nil
	} else {
		err := errors.New("队列中无元素")
		return nil, err
	}
}

// 展示队列中的所有数据
func ShowDataFromTabulationInRam(sqList *model.SqList) {
	for i := 0; i < sqList.Length; i++ {
		fmt.Println("message: ", sqList.DataList[i].Message, " ,time: ", sqList.DataList[i].CreateTime)
		time.Sleep(1)
	}
}

// 定义一个将队列中数据前移的方法
func makeTabulationLeft(sqList *model.SqList) {
	for i := 0; i < sqList.Length-1; i++ {
		sqList.DataList[i] = sqList.DataList[i+1]
	}
}

func InsertTabulationInDisk(data string, sqList *model.SqListDisk) {
	myData := model.MyData{
		Message:    data,
		CreateTime: time.Now(),
	}
	dataJson, err := json.Marshal(myData)
	if err != nil {
		log.Fatalln(err.Error())
		return
	}
	//err = ioutil.WriteFile(sqList.DataFilePath, dataJson, 066)
	file, err := os.OpenFile(sqList.DataFilePath, os.O_WRONLY|os.O_APPEND, 0666)
	if err != nil {
		log.Fatalln(err.Error())
		return
	}
	defer func(file *os.File) {
		err := file.Close()
		if err != nil {
			log.Fatalln(err.Error())
		}
	}(file)
	writer := bufio.NewWriter(file)
	defer func(writer *bufio.Writer) {
		err := writer.Flush()
		if err != nil {
			log.Fatalln(err.Error())
		}
	}(writer)
	_, err = writer.Write(dataJson)
	if err != nil {
		log.Fatalln(err.Error())
	}
	_, err = writer.Write([]byte("\n"))
	if err != nil {
		log.Fatalln(err.Error())
	}
	if sqList.Lines < sqList.Length {
		sqList.Lines++
	} else {
		file, err := os.OpenFile(sqList.DataFilePath, os.O_RDWR|os.O_CREATE, 0666)
		if err != nil {
			log.Fatalln(err.Error())
		}
		defer func(file *os.File) {
			err := file.Close()
			if err != nil {
				log.Fatalln(err.Error())
			}
		}(file)
		_, err = popLine(file)
		if err != nil {
			log.Fatalln(err)
		}
	}
}

func GetDataFromTabulationInDisk(sqList *model.SqListDisk) (*model.MyData, error) {
	if sqList.Lines > 0 {
		sqList.Lines--
		file, err := os.OpenFile(sqList.DataFilePath, os.O_RDWR|os.O_CREATE, 0666)
		if err != nil {
			log.Fatalln(err.Error())
		}
		defer func(file *os.File) {
			err := file.Close()
			if err != nil {
				log.Fatalln(err.Error())
			}
		}(file)
		line, err := popLine(file)
		var myData model.MyData
		err1 := json.Unmarshal(line, &myData)
		if err1 != nil {
			log.Fatalln(err1)
		}
		return &myData, err
	} else {
		err := errors.New("队列中无元素")
		return nil, err
	}
}

func ShowDataFromTabulationInDisk(sqList *model.SqListDisk) {
	file, err := os.Open(sqList.DataFilePath)
	if err != nil {
		log.Fatalln(err.Error())
	}
	defer func(file *os.File) {
		err := file.Close()
		if err != nil {
			log.Fatalln(err.Error())
		}
	}(file)
	fd := bufio.NewReader(file)
	for {
		readString, err := fd.ReadString('\n')
		if err != nil {
			return
		}
		var myData model.MyData
		s := strings.Split(readString, "\n")[0]
		err = json.Unmarshal([]byte(s), &myData)
		if err != nil {
			log.Fatalln(err.Error())
		}
		fmt.Println(myData)
		time.Sleep(1)
	}
}
func popLine(f *os.File) ([]byte, error) {
	fi, err := f.Stat()
	if err != nil {
		return nil, err
	}
	buf := bytes.NewBuffer(make([]byte, 0, fi.Size()))

	_, err = f.Seek(0, io.SeekStart)
	if err != nil {
		return nil, err
	}
	_, err = io.Copy(buf, f)
	if err != nil {
		return nil, err
	}

	line, err := buf.ReadBytes('\n')
	if err != nil && err != io.EOF {
		return nil, err
	}

	_, err = f.Seek(0, io.SeekStart)
	if err != nil {
		return nil, err
	}
	nw, err := io.Copy(f, buf)
	if err != nil {
		return nil, err
	}
	err = f.Truncate(nw)
	if err != nil {
		return nil, err
	}
	err = f.Sync()
	if err != nil {
		return nil, err
	}

	_, err = f.Seek(0, io.SeekStart)
	if err != nil {
		return nil, err
	}
	return line, nil
}
